package com.kvn.reflect.benchmark;

import com.kvn.reflect.MethodAccess;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

/**
 * Beanchmark 参考： https://github.com/alibaba/Sentinel/blob/master/sentinel-benchmark/src/main/java/com/alibaba/csp/sentinel/benchmark/SentinelEntryBenchmark.java
 * Created by wangzhiyuan on 2019/1/29
 */
@Warmup(iterations = 3, time = 10)
@Measurement(iterations = 3, time = 10)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
public class MethodInvokeBeanchmark {

    @Param({"25", "50", "100", "200"})
    private int length;

    private List<Integer> numbers;

    private MethodInvokeBeanchmark target;

    @Setup
    public void prepare() {
        target = this;
        numbers = new ArrayList<>();
        for (int i = 0; i < length; i++) {
            numbers.add(ThreadLocalRandom.current().nextInt());
        }
    }

    public void doSomething() {
//        Collections.shuffle(numbers);
        Collections.sort(numbers);
    }

    @Benchmark
    @Threads(1)
    public void test1ThreadDoSomethingWithJdkReflect() throws Exception {
        MethodInvokeBeanchmark.class.getMethod("doSomething").invoke(target);
    }

    @Benchmark
    @Threads(1)
    public void test1ThreadDoSomethingWithHpReflect(){
        MethodAccess.get(MethodInvokeBeanchmark.class).invoke(target, "doSomething");
    }

    @Benchmark
    @Threads(2)
    public void test2ThreadsDoSomethingWithJdkReflect() throws Exception {
        MethodInvokeBeanchmark.class.getMethod("doSomething").invoke(target);
    }

    @Benchmark
    @Threads(2)
    public void test2ThreadsDoSomethingWithHpReflect(){
        MethodAccess.get(MethodInvokeBeanchmark.class).invoke(target, "doSomething");
    }

    @Benchmark
    @Threads(4)
    public void test4ThreadsDoSomethingWithJdkReflect() throws Exception {
        MethodInvokeBeanchmark.class.getMethod("doSomething").invoke(target);
    }

    @Benchmark
    @Threads(4)
    public void test4ThreadsDoSomethingWithHpReflect(){
        MethodAccess.get(MethodInvokeBeanchmark.class).invoke(target, "doSomething");
    }


    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(MethodInvokeBeanchmark.class.getSimpleName())
                .forks(1) // 启动测试进程数，比如@Fork(2)，那么JMH会启动两个进程进行测试
                .output("E:/MethodInvokeBeanchmark.log")
                .build();

        new Runner(opt).run();
    }
}
